TabView

Scripting 提供了与最新 iOS TabView 体系一致的 API: 通过 TabViewTabTabSection 组织界面结构,使应用能够在 iOS 18+ 环境下完整支持多标签视图、侧边栏标签、可定制布局等。

相比旧版本依赖 tabItem 修饰符的方式,新的结构更加灵活、分组更清晰,并能与 TabViewCustomization 等新特性无缝配合。


一、基础结构:TabView + Tab

在最基本的形式中,TabView 作为容器,内部包含多个 Tab。 每个 Tab 定义:

  • 标签标题
  • 图标
  • 标识值(value)
  • 角色(如 search)
  • 对应的内容视图

示例:

1function RootView() {
2  const selection = useObservable<number>(0)
3
4  return (
5    <TabView selection={selection}>
6      <Tab
7        title="首页"
8        systemImage="house.fill"
9        value={0}
10      >
11        <HomeView />
12      </Tab>
13
14      <Tab
15        title="搜索"
16        systemImage="magnifyingglass"
17        value={1}
18        role="search"
19      >
20        <SearchView />
21      </Tab>
22
23      <Tab
24        title="设置"
25        systemImage="gearshape.fill"
26        value={2}
27      >
28        <SettingsView />
29      </Tab>
30    </TabView>
31  )
32}

要点:

  • selection 通过 Observable 控制当前激活的标签
  • value 必须与 selection 的泛型类型匹配(string 或 number)
  • Search Tab 可使用 role="search" 与搜索相关行为联动

二、使用 TabSection 组织分组标签

当 Tab 数量较多、需要按功能分类、需要在侧边栏中显示复杂结构时,可以使用 TabSection

结构关系为:

TabView ├─ TabSection │ ├─ Tab │ ├─ Tab │ └─ ... ├─ TabSection │ ├─ Tab │ └─ ...

1. 使用 title 作为分组标题

1<TabView selection={selection}>
2  <TabSection title="收件箱">
3    <Tab title="收件箱" systemImage="tray.fill" value="inbox">
4      <InboxView />
5    </Tab>
6    <Tab title="已发送" systemImage="paperplane.fill" value="sent">
7      <SentView />
8    </Tab>
9  </TabSection>
10
11  <TabSection title="标签">
12    <Tab title="重要" systemImage="star.fill" value="important">
13      <ImportantView />
14    </Tab>
15  </TabSection>
16</TabView>

2. 使用 header 作为自定义组头

如需显示图标、说明文字或复合内容,可用 header

1<TabSection
2  header={
3    <HStack spacing={8}>
4      <Image systemName="folder.fill" />
5      <VStack>
6        <Text fontWeight="bold">项目</Text>
7        <Text fontSize={12} foregroundColor="secondary">
8          最近打开的项目
9        </Text>
10      </VStack>
11    </HStack>
12  }
13>
14  <Tab title="项目 A" systemImage="doc.fill" value="projectA">
15    <ProjectAView />
16  </Tab>
17</TabSection>

三、TabSection 的高级能力:布局、操作区、拖拽与可见性

TabSection 提供了丰富的分组级配置,让 Tab 分组的呈现方式更加灵活。

1. tabPlacement(标签位置策略)

支持:

  • automatic
  • pinned
  • sidebarOnly

例如将某组仅显示在侧边栏:

1<TabSection title="标签" tabPlacement="sidebarOnly">
2  <Tab title="重要" systemImage="star.fill" value="important">
3    <ImportantView />
4  </Tab>
5</TabSection>

2. sectionActions(分组操作区)

为某一组提供额外操作按钮:

1<TabSection
2  title="列表"
3  sectionActions={
4    <Button title="添加" systemImage="plus" action={addItem} />
5  }
6>
7  ...
8</TabSection>

3. 分组可见性与可定制行为

通过:

  • defaultVisibility
  • customizationID
  • customizationBehavior
  • draggable
  • dropDestination

可以为每个分组提供:

  • 默认显示策略
  • 是否允许用户自定义排序或隐藏
  • 是否可以拖动
  • 外部拖拽数据的处理

例如:

1<TabSection
2  title="文件"
3  customizationID="file-section"
4  customizationBehavior="reorderable"
5  draggable="file-section"
6  dropDestination={items => handleDrop(items)}
7>
8  ...
9</TabSection>

四、TabView 级别的高级配置

TabView 本身提供了一系列属性,可用于构建高级 UI(iOS 18~26)。

包括:

  • tabBarMinimizeBehavior
  • tabViewBottomAccessory
  • tabViewSearchActivation
  • tabViewCustomization
  • tabViewSidebarHeader
  • tabViewSidebarFooter
  • tabViewSidebarBottomBar

以下为每项能力的说明。


1. tabBarMinimizeBehavior(iOS 26.0+)

控制 TabBar 是否根据滚动方向自动最小化:

  • automatic
  • never
  • onScrollDown
  • onScrollUp

示例:

1<TabView
2  selection={selection}
3  tabBarMinimizeBehavior="onScrollDown"
4>
5  ...
6</TabView>

2. tabViewBottomAccessory(iOS 26.0+)

为 TabView 添加底部附加视图,例如提示栏:

1<TabView
2  selection={selection}
3  tabViewBottomAccessory={
4    <HStack>
5      <Text>左右滑动切换标签</Text>
6      <Spacer />
7      <Button title="知道了" action={dismiss} />
8    </HStack>
9  }
10>
11  ...
12</TabView>

3. tabViewSearchActivation(iOS 26.0+)

控制搜索 Tab 的激活方式:

  • automatic
  • searchTabSelection

role="search" 搭配使用:

1<TabView
2  selection={selection}
3  tabViewSearchActivation="searchTabSelection"
4>
5  ...
6</TabView>

4. 侧边栏附属视图(iOS 18.0+)

包括:

  • tabViewSidebarHeader
  • tabViewSidebarFooter
  • tabViewSidebarBottomBar

示例:

1<TabView
2  selection={selection}
3  tabViewSidebarHeader={<UserHeader />}
4  tabViewSidebarFooter={<SettingsButton />}
5  tabViewSidebarBottomBar={<UpgradeButton />}
6>
7  ...
8</TabView>

五、TabViewCustomization:标签页可定制化体系(重点补充)

TabViewCustomization 是一个可序列化的状态对象,用于存储和恢复用户对 Tab 布局的自定义行为,包括:

  • Tab 分组顺序
  • 分组内部的 Tab 排序
  • Tab 可见性(在 TabBar 与 Sidebar 中分别独立管理)
  • 重置各种设置
  • 持久化与恢复

它通常放在 TabView 根视图中,通过:

1tabViewCustomization={customizationState}

来注入。

1. 创建与加载 TabViewCustomization

创建方式通常是:

1const customization = useObservable<TabViewCustomization >(() => {
2  const data = Storage.get('tab_customization')
3  if (data) {
4    return TabViewCustomization.fromData(data) ?? new TabViewCustomization()
5  }
6  return new TabViewCustomization()
7})
8
9useEffect(() => {
10  const listener = (newValue: TabViewCustomization) => {
11    const data = newValue.toData()
12    if (data) {
13      Storage.set('tab_customization', data)
14    }
15  }
16  customization.subscribe(listener)
17  return () => {
18    customization.unsubscribe(listener)
19  }
20}, [])

如需创建一个新的空自定义对象,可使用:

1const customizationState = useObservable(() => new TabViewCustomization())

2. 保存自定义内容

你可以将用户调整后的 Tab 布局序列化保存:

1const data = customization.value?.toData()
2Storage.set('tab_customization', data)

toData() 会将内部状态转换为可存储的 Data 对象。

3. 获取并操作分组(Section)

1getSection(id: string): TabViewCustomizationSection | null

TabSection 通常带有 customizationID,这样就可以获取特定分组并操作它:

1const section = customization.value?.getSection('file-section')
2
3section?.tabOrder        // 一个包含 tab ID 顺序的数组,或 null
4section?.resetTabOrder() // 重置排序

场景示例:

  • 用户将“文件”分组中的 Tab 重新排序
  • 用户将某些 Tab 移动到“更多”区域
  • 应用需要根据用户排序更新 UI

4. 获取并操作单个 Tab

1getTab(id: string): TabViewCustomizationTab | null

可通过 Tab 的 customizationID 获取并调整其可见性:

1const tab = customization.value?.getTab('important-tab')
2
3tab?.tabBarVisibility         // Visibility 类型
4tab.sidebarVisibility = 'hidden'

适用场景:

  • 控制 Tab 在 TabBar 或 Sidebar 中是否显示
  • 用户可通过自定义界面操作 Tab 可见性
  • 程序自动隐藏某些 Tab

5. 全局重置

1resetSectionOrder(): void
2resetVisibility(): void

通常用于:

  • 点击“恢复默认布局”按钮
  • 版本更新后清理已有布局逻辑

示例:

1<Button
2  title="恢复默认"
3  action={() => {
4    customization.value?.resetSectionOrder()
5    customization.value?.resetVisibility()
6  }}
7/>

六、与旧的 tabItem 写法的关系

此文档采用全新的结构化写法:

  • TabView
  • Tab
  • TabSection
  • TabViewCustomization

旧的 tabItem 写法仍可用于简单场景以及兼容iOS 17,但与侧边栏、Tab 分组、自定义布局等高级能力不兼容。 在复杂应用中,建议全面迁移到新的组件体系。